package cn.xinfei.xdecision.engine.pre;

import cn.xinfei.xdecision.common.utils.constant.Constants;
import cn.xinfei.xdecision.constant.EngineResultEnum;
import cn.xinfei.xdecision.constant.GlobalConstant;
import cn.xinfei.xdecision.engine.runner.context.BranchTypeEnum;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.service.DecisionIdBuilder;
import cn.xinfei.xdecision.model.DecisionApiRequest;
import cn.xinfei.xdecision.model.DecisionApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@Component
@Aspect
public class PreProcessAspect {

    @Value("${xdecision.reentry.timeout}")
    private volatile int reentryTimeout = 60;


    @Autowired
    private DecisionIdBuilder decisionIdBuilder;

    /**
     * 方法用途（切入点表达式可以用&&,||,!来组合使用）:
     * execution: 匹配连接点：execution(* com.example.demo.*(..))--com.example.demo包下所有类的方法
     * within: 某个类里面
     * this: 指定AOP代理类的类型
     * target:指定目标对象的类型
     * args: 指定参数的类型
     * bean:指定特定的bean名称，可以使用通配符（Spring自带的）
     *
     * @target： 带有指定注解的类型
     * @args: 指定运行时传的参数带有指定的注解
     * @within: 匹配使用指定注解的类
     * @annotation:指定方法所应用的注解 <br/>
     * 操作步骤: TODO<br/>
     * ${tags}
     */
    @Pointcut("@annotation(cn.xinfei.xdecision.engine.pre.PreProcess)")
    public void logTracer() {
    }

    /**
     * 方法用途:
     * 在AnnotationDemo注解之前执行
     * 标识一个前置增强方法，相当于BeforeAdvice的功能.
     * <br/>
     * 操作步骤: TODO<br/>
     * ${tags}
     */
    @Before("@annotation(cn.xinfei.xdecision.engine.pre.PreProcess)")
    public void beforeRun(JoinPoint joinPoint) {

    }

    /**
     * 方法用途:
     *
     * @Around 环绕增强，相当于MethodInterceptor，对带@AnnotationDemo注解的方法进行切面，并获取到注解的属性值
     * ProceedingJoinPoint: 环绕通知
     * <br/>
     * 操作步骤: TODO<br/>
     * ${tags}
     */
    @Around("@annotation(preProcess)")
    public Object around(ProceedingJoinPoint joinPoint, PreProcess preProcess) {
        Object obj = null;
        try {
            XDecisionContext.destroy();
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            String requestId = null;
            String scene = null;
            String tpCode = null;
            String sign = null;
            String bizEnc = null;
            if (requestAttributes != null) {
                HttpServletRequest request = requestAttributes.getRequest();
                requestId = request.getHeader(Constants.REQUEST_ID);
                scene = request.getHeader(Constants.SCENE);
                tpCode = request.getHeader(Constants.TP_CODE);
                sign = request.getHeader(Constants.SIGN);
                bizEnc = request.getHeader(Constants.BIZ_ENC);
            }

            obj = before(requestId, scene, tpCode, sign, bizEnc, joinPoint);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            log.error("error={}", throwable.getMessage(), throwable);
        } finally {
            XDecisionContext.destroy();
            MDC.remove(GlobalConstant.NID);
            MDC.remove(GlobalConstant.PID);
            MDC.remove(GlobalConstant.TRACE_ID);
        }
        return obj;
    }

    public Object before(String requestId, String scene, String tpCode, String sign, String bizEnc, ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = null;
        DecisionApiResponse decisionApiResponse = new DecisionApiResponse();

        Object[] args = joinPoint.getArgs();
        DecisionApiRequest decisionApiRequests = (DecisionApiRequest) args[0];
        if (StringUtils.isEmpty(requestId)) {
            requestId = decisionApiRequests.getRequestId();
        }

        MDC.put(GlobalConstant.TRACE_ID, requestId);
        log.info("接收到请求，requestId={},tpCode={}", requestId, tpCode);

        if (StringUtils.isEmpty(requestId)) {
            decisionApiResponse.setEngineResult(EngineResultEnum.NOT_ALLOW_REQUEST_ID_NULL);
            log.info("requestId 不允许为null");
            obj = decisionApiResponse;
            return decisionApiResponse;
        }
        if (StringUtils.isEmpty(tpCode)) {
            tpCode = decisionApiRequests.getTpCode();
        }
        if (StringUtils.isEmpty(tpCode)) {
            decisionApiResponse.setEngineResult(EngineResultEnum.NOT_ALLOW_TP_CODE_NULL);
            log.info("tpCode 不允许为null");
            obj = decisionApiResponse;
            return decisionApiResponse;
        }

        if (StringUtils.isEmpty(scene)) {
            scene = decisionApiRequests.getScene();
        }
        if (StringUtils.isEmpty(scene)) {
            scene = tpCode;
        }
        Long organId = decisionApiRequests.getOrganId();
        PipelineContextHolder.setOrganId(organId.intValue());
        //幂等拦截: 获取分布式锁，
        // 如果存在bit位，则可能是重复请求，需要再次判断以下数据库(hash冲突的情况，非常小)
        // 如果不存在bit，则一定不是重复请求
//            Boolean getbit = RedisLock.getbit(XEngineConstant.IDEMPOTENT_KEY, Math.abs(requestId.hashCode()));
//            if (getbit) {
//                //幂等拦截
//                DecisionResult decisionResult = decisionParamCheckServer.idempotent(tpCode, requestId);
//                if (decisionResult != null) {
//                    decisionApiResponse.setEngineResult(EngineResultEnum.getByCode(decisionResult.getCode()));
//                    decisionApiResponse.setRequestId(decisionResult.getRequestId());
//                    decisionApiResponse.setDecisionId(decisionResult.getDecisionId());
//                    decisionApiResponse.setEngineCode(decisionResult.getEngineCode());
//                    log.info("tpCode={},requestId={},请求已经存在,decisionResult={}", tpCode, requestId, JsonUtils.toJsonString(decisionResult));
//                    obj = decisionApiResponse;
//                    return decisionApiResponse;
//                }
//                log.warn("布隆过滤器存在，而数据库不存在。requestId={}", requestId);//概率极低
//            }


        String decisionId = decisionIdBuilder.create(scene);
        MDC.put(GlobalConstant.PID, decisionId);
        MDC.put(GlobalConstant.BRANCH, BranchTypeEnum.MAIN.name());

        PipelineContextHolder.setDecisionId(decisionId);
        PipelineContextHolder.setRequestId(requestId);
        PipelineContextHolder.setScene(scene);
        PipelineContextHolder.setTpCode(tpCode);

        // AnnotationDemo注解的属性值
        Signature signature = joinPoint.getSignature();

        // obj是返回的结果，joinPoint.proceed用于启动目标方法执行，非常重要
        obj = joinPoint.proceed(args);

        return obj;
    }

}
